package com.lei.study.cds.unit3;

import com.lei.study.cds.unit2.MvelCalculateTools;
import org.apache.commons.lang3.StringUtils;
import org.mvel2.MVEL;
import org.mvel2.ParserContext;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 伍磊
 */
public class ParseOver {

    public static void main(String[] args) {
        // 需要解析的公式
        String formula = "HAVG(REV/MAX(REB,REC), 5)";

        /*解析后的公式:
        AVG(R5T0SEP01/MAX(R5T0SEP02,R5T0SEP03),R4T0SEP01/MAX(R4T0SEP02,R4T0SEP03),R3T0SEP01/MAX(R3T0SEP02,R3T0SEP03),R2T0SEP01/MAX(R2T0SEP02,R2T0SEP03),R1T0SEP01/MAX(R1T0SEP02,R1T0SEP03))
         */

        // 通过成对括号判断
        // 1 获取HAVG后面的字符串
        String x = StringUtils.substringAfter(formula, "HAVG");
        // 2 HAVG后面第一个是左括号 那么判断此左括号的成对右括号即可
        int count = 0;
        boolean flag = false;
        String sp = null;
        for (int i = 0; i < x.length(); i++) {
            if (count < 0) {
                break;
            }
            // 临时变量，遍历的目标字符串字符
            String temp = x.substring(i, i + 1);
            // 左括号，计数器+1
            if (StringUtils.equals(temp, "(")) {
                count++;
                flag = true;
            }
            // 右括号，计数器-1
            if (StringUtils.equals(temp, ")")) {
                count--;
            }

            // 找到第一个就好
            if (flag && count == 0 && i != 0) {
                sp = StringUtils.substring(x, 0, i);
                break;
            }
        }

        sp = "HAVG" + sp + ")";
        System.out.println(sp);

        String offset = StringUtils.substringAfterLast(sp, ",");

        // 获取需要解析的完整原公式
        // 1 获取所有类型为20的函数 此类函数属于解析函数
        // 2 从1中所有的函数对此公式进行循环 contains 判断哪些函数使用
        // 3 使用的函数中挑选出特殊函数进行循环解析 这里为了测试使用 HAVG作为特殊解析函数

        // 根据HAVG和 , 加 , 后的第一个小括号 确定一整个需要替换的函数 需要注意的是可能有多个所以需要循环替换
        // 1
        String havg = StringUtils.substringBetween(formula, "HAVG", ",");
        // 2
        String afterHavg = StringUtils.substringAfter(formula, "HAVG" + havg + ",");
        // 3
        String s = StringUtils.substringBefore(afterHavg, ")");
        // 4 组合
        String special = "HAVG" + havg + "," + s + ")";


        System.out.println("原公式: "+ sp);
        System.out.println("--------");

        System.out.println("使用演算值均为1");
        System.out.println("--------");

        // 时间偏移量 3
        int timeOffset = Integer.parseInt(offset.replace(")", "").trim());

        // 使用解析函数 HAVG
        String function = "HAVG";

        // 需要替换的参数
        String replaceParam = StringUtils.substringBeforeLast(sp, ",");
        replaceParam = replaceParam.replace("HAVG(", "");

        // 三个参数信息
        ParamDTO paramDTO1 = new ParamDTO("REV", "1", "", BigDecimal.ONE);
        ParamDTO paramDTO2 = new ParamDTO("REB", "2", "", BigDecimal.ONE);
        ParamDTO paramDTO3 = new ParamDTO("REC", "3", "", BigDecimal.TEN);
        ParamDTO paramDTO4 = new ParamDTO("REVX", "4", "", BigDecimal.ONE);
        ParamDTO paramDTO5 = new ParamDTO("RECX", "5", "", BigDecimal.ONE);
        ParamDTO paramDTO6 = new ParamDTO("RTEC", "6", "", BigDecimal.ONE);

        List<ParamDTO> params = new ArrayList<>();
        params.add(paramDTO1);
        params.add(paramDTO2);
        params.add(paramDTO3);
        params.add(paramDTO4);
        params.add(paramDTO5);
        params.add(paramDTO6);

        // 解析函数匹配的计算函数
        String matchFunction = "AVG";

        // 2 进行解析
        // 实际参数集合
        List<String> wholeParams = new ArrayList<>();
        // 参数对应演算值
        Map<String, BigDecimal> map = new HashMap<>(16);
        // 替换字符串 --> 解析参数匹配的计算参数AVG
        String replaceExpress = matchFunction + "(";

        // 2-1 根据时间偏移量获取到实际的参数
        for (int i = timeOffset; i > 0; i--) {
            // 基础参数
            String basic = replaceParam;

            // 将三个参数进行时间参数的转换  这里demo简单点，先不考虑参数中时间参数不为空的情况
            for (ParamDTO paramDTO : params) {
                // 被替换的字符串
                String paramName = paramDTO.getParamName();

                // 替换字符串   T0SEP0 - 分隔符
                String replace = "R" + i + "T0SEP0" + paramDTO.getProjectId();

                // replace实际计算的参数 添加到参数集合
                wholeParams.add(replace);
                map.put(replace, paramDTO.getValue());

                // 替换
                basic = basic.replaceAll(paramName, replace);

            }

            // 添加到replaceExpress
            if (i != 1) {
                replaceExpress = replaceExpress + basic + ",";
            } else {
                replaceExpress = replaceExpress + basic + ")";
            }

        }

        // 替换字符串
        formula = StringUtils.replace(formula, sp, replaceExpress);
        System.out.println("解析后的公式: " + formula);
        System.out.println("--------");

        System.out.print("实际参数集合: ");
        wholeParams.forEach(param -> System.out.print(param + "、"));
        System.out.println();
        System.out.println("--------");

        // 计算
        ParserContext parserxt = MvelCalculateTools.initParse();

        Serializable compiled = MVEL.compileExpression(formula, parserxt);

        BigDecimal o = (BigDecimal) MVEL.executeExpression(compiled, map);
        System.out.println("计算结果: " + o);
    }


}
